package com.googlecode.vmock.matchers;

import java.lang.reflect.Array;
import java.util.LinkedHashSet;

import com.googlecode.vmock.matchers.ranking.MatcherRanking;
import com.googlecode.vmock.utils.ArrayUtils;

/**
 * A matcher that matches a value if and only if that value is present in a given set of accepted values. The 'is present' check is made by calling
 * {@link Object#equals(Object)} on every element of a given array.
 * <p>
 * This matcher is null-safe.
 * 
 * @author marcin.jagus
 * @param <T> Type of accepted values
 */
public class DisjunctionMatcher<T> extends AbstractMatcher {

    static {
        MatcherRanking.setRanking(DisjunctionMatcher.class, MatcherRanking.DISJUNCTION_MATCHER_DEFAULT_RANKING);
    }

    /**
     * Array of accepted values.
     */
    private T[] acceptedValues;

    /**
     * Creates new disjunction matcher for a given array of accepted values.
     * 
     * @param acceptedValues array of accepted values
     */
    @SuppressWarnings("unchecked")
    public DisjunctionMatcher(T... acceptedValues) {
        LinkedHashSet<T> set = new LinkedHashSet<T>(acceptedValues.length);
        for (T value : acceptedValues) {
            set.add(value);
        }

        this.acceptedValues = set.toArray((T[]) Array.newInstance(acceptedValues.getClass().getComponentType(), 0));
    }

    /**
     * Returns <code>true</code> if a value is one of the accepted values of this matcher.
     */
    @Override
    public boolean matches(Object value) {
        return ArrayUtils.contains(acceptedValues, value);
    }

    /**
     * Compares this matcher to another matcher. If both matchers have the same ranking, meaning they are of the same type, a matcher with lower
     * number of accepted values takes the priority.
     */
    @Override
    public int compareTo(Matcher o) {
        int compareResult = super.compareTo(o);
        if (compareResult == 0) {
            return this.acceptedValues.length - ((DisjunctionMatcher<?>) o).acceptedValues.length;
        } else {
            return compareResult;
        }
    }

    @Override
    public String toString() {
        return String.format("DisjunctionMatcher(%s)", ArrayUtils.toString(acceptedValues));
    }
}
